
#ifndef _TTG_DEVICES_DEVICE_
#define _TTG_DEVICES_DEVICE_
#include "Common/Definitions.h"

#include <vector>
#include "Common/Structures.h"

#include "Identifiers.h"
#include "Maps.h"

namespace ttg
{
	namespace devices
	{
		//Definition of used class.
		class Api;

		//Base class for any logical device.
		//All descendants should be implemented as singletons.
		class Device :public ttg::structures::NamedObject
		{
			private:
				static LogicalDeviceMap<std::pair<Device *, Api *>> *devices_plus_apis;
				static std::vector<Device *> *devices;
				//Returns reference to instance.
				static inline LogicalDeviceMap<std::pair<Device *, Api *>> *getDevicesPlusApis()
				{ return devices_plus_apis == NULL
										? (devices_plus_apis = new LogicalDeviceMap<std::pair<Device *, Api *>>())
										: devices_plus_apis; }
				//Returns reference to instance.
				static inline std::vector<Device *> *getDevices()
				{ return devices == NULL
								? (devices = new std::vector<Device *>())
								: devices; }
			private:
				ttg::devices::DeviceID ID;
				ttg::structures::Dictionary<Api> *apis;
			private:
				//Registeres api.
				//Will be called by Api constructor.
				void registerApi(Api *api);
			protected:
				//Creates device with required name + ID and registeres it.
				inline Device(const char *name, ttg::devices::DeviceID ID)
					:ttg::structures::NamedObject(name)
				{
					apis = ttg::structures::Dictionary<Api>::create(20);
					this->ID = ID;
					getDevices()->push_back(this);
				}
			public:
				//Returns unique ID of this device.
				inline ttg::devices::DeviceID getID()
				{ return ID; }
				//Returns API with requred name (or NULL).
				Api *findApi(const char *name)
				{ return apis->get(name); }
				//Returns all registered APIs.
				inline std::vector<Api *> getAllApis()
				{ return apis->toVector(); }
				//Returns all known devices.
				static inline const std::vector<Device *> &getAllDevices()
				{ return *getDevices(); }
				//Returns reference to device, that corresponds to requred type.
				static inline Device *getDevice(LogicalDevice type)
				{ return (*getDevicesPlusApis())[type].first; }
				//Returns reference to API, that corresponds to requred type.
				static inline Api *getApi(LogicalDevice type)
				{ return (*getDevicesPlusApis())[type].second; }
				~Device()
				{ SAFE_DELETE(apis); }
			friend class Api;
		};

		//Base class for any API-wrapper. Provides some basic information about it.
		//All descendants should be implemented as singletons.
		class Api :public ttg::structures::NamedObject
		{
			private:
				Device *owner;
				ttg::devices::ApiID ID;
			protected:
				//Creates named API for required owner.
				//Note: this API will be registered in device.
				inline Api(const char *name, ttg::devices::ApiID ID, Device *owner)
					:ttg::structures::NamedObject(name)
				{
					this->ID = ID;
					this->owner = owner;
					owner->registerApi(this);
				}
			public:
				//Returns unique ID of this API.
				inline ttg::devices::ApiID getID()
				{ return ID; }
				//Returns owner device.
				inline Device *getOwner()
				{ return owner; }
				//Returns true, if API is available on this machine.
				virtual bool isAvailable() = 0;
				//Returns true, if additional CPU-thread is requred.
				//It means, that corresponded task will use CPU-thread as well as this device.
				virtual bool isAccelerator() = 0;
				//Returns count of tasks, that can be executed simultaneously via this API.
				virtual size_t getConcurrencyLevel() = 0;
		};
	}
}

#endif
